// 注意:
// 默认需要让其他构造函数继承默认无参的构造函数，因为需要初始化 m_ptr 和 m_length
// 对于流式回调一般不会设置这两个字段，导致构造出来的 cxx::m_ptr 和 m_length 是未被设置的状态
#ifndef xpack_lang_cxx_strlize
#define xpack_lang_cxx_strlize
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::lang_cxx::inc
#include"define/base_type.hpp"
#include"define/inf.hpp"
#include"define/nan.hpp"
#include"lang/cxx.hpp"
#include"macro/xunlikely.hpp"
#include"math/abs.hpp"
#include"math/literal.hpp"
#include"math/exp10.hpp"
#include"math/exp10r.hpp"
#include"math/maximum.hpp"
#include"math/minimum.hpp"
#include"math/numeration_t.hpp"
#include"memop/reverse.hpp"
#include"memop/copy.hpp"
#include"memop/fill.hpp"
#include"meta/is_float.hpp"
#include"meta/is_integer.hpp"
#include"meta/more_fit.hpp"
#include"meta/unsigned_type.hpp"
#include"utils/mfxx.hpp"
#pragma pop_macro("xuser")

namespace mixc::lang_cxx{
    namespace inc{
        using namespace literal;
    }

    enum class float_format_t{
        // 注意：不要改变枚举的顺序，下文依赖该顺序
        // 指数部分显示正负号 实数部分显示正负号
        // 0                  0                     fmt_1p2e3
        // 0                  1                     fmt_s1p2e3
        // 1                  0                     fmt_1p2es3
        // 1                  1                     fmt_s1p2es3
        fmt_1p2e3          , // "1.2e3"
        fmt_s1p2e3         , // "+1.2e3"
        fmt_1p2es3         , // "1.2e+3"
        fmt_s1p2es3        , // "+1.2e+3"
        fmt_1p2E3          , // "1.2E3"
        fmt_s1p2E3         , // "+1.2E3"
        fmt_1p2Es3         , // "1.2E+3"
        fmt_s1p2Es3        , // "+1.2E+3"
        fmt_n              , // "n"
        fmt_sn             , // "+n"
    };

    enum class int_format_t{
        fmt_n               ,
        fmt_sn              ,
    };

    constexpr char lower[]                  = "0123456789abcdefghijklmnopqrstuvwxyz";
    constexpr char upper[]                  = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    constexpr bool is_scientific_notation   = true;
    constexpr auto force_real_part_sign     = uxx(float_format_t::fmt_s1p2e3);
    constexpr auto force_exp_part_sign      = uxx(float_format_t::fmt_1p2es3);
    constexpr auto force_upper_e            = uxx(float_format_t::fmt_1p2E3);

    template<class functor_t, class item_t>
    concept can_cxx_format = inc::can_callback<
        functor_t,
        cxx<item_t>(
            cxx<item_t>       real,
            uxx               expanding_zeros,
            uxx               leading_zeros,
            cxx<item_t>       decimal,
            uxx               trailing_zeros,
            cxx<item_t>       exp
        )
    >;

    template<class item_t, inc::is_float float_t, class cxx_t = cxx<item_t>>
    inline void strlize_core(float_t value, bool is_scientific_notation, uxx precious, auto const & cxx_format){
        using mfxx = inc::mfxx<float_t>;
        enum{
            max_step = inc::mf80::precious() + 1,
        };

        #define xgen(V,STR)                                         \
        if (value == V){                                            \
            cxx_t real = value < 0 ? "-" STR : "+" STR;             \
            return cxx_format(real, 0, 0, "", 0, "");               \
        }

        xgen(inc::nan, "nan")
        xgen(inc::inf, "inf")
        #undef  xgen

        char real_part[max_step];
        char decimal_part[max_step];
        char exp_part[8];
        auto real_exp10     = uxx{};
        auto dec_exp10      = uxx{};
        bool is_neg         = value < 0;
        auto p_real         = real_part + 1;
        auto p_dec          = decimal_part;
        auto p_exp          = exp_part;

        if (is_neg){
            value           = -value;
            real_part[0]    = '-';
        }
        else{
            real_part[0]    = '+';
        }

        if (is_scientific_notation){
            auto e          = float_t(inc::log10_2) * mfxx{value}.real_exp();
            auto exp        = ixx(e);
            auto is_neg_e   = false;

            if (exp < 0){
                // exp 可能小于 -308，
                // 1e-310 这样的数是可以表示的，但 1e310 是无法表示的（会变成 inf）
                // 导致 exp10_unsafe(exp) 的结果变成 inf，value *= inf 也会等于 inf
                exp         = -exp;

                // 这里需要分成两部分，避免浮点误差
                value      *= inc::exp10_unsafe<float_t>(uxx(exp / 2));
                value      *= inc::exp10_unsafe<float_t>(uxx(exp - exp / 2)); // 相当于 value *= 10^(exp)
                p_exp[0]    = '-';
                p_exp      += 1;
                is_neg_e    = true;
            }
            else if (exp > 0){
                value      *= inc::exp10r_unsafe<float_t>(uxx(exp)); // 相当于 value *= 1/(10^exp)
                p_exp[0]    = '+';
                p_exp      += 1;
            }
            else{
                p_exp[0]    = '+';
                p_exp      += 1;
            }

            if (value != 0 and value < float_t(1.0)){
                value      *= 10;
                exp        += is_neg_e ? +1 : -1;
            }
            else if (value >= float_t(10.0)){
                value      *= float_t(0.1);
                exp        += is_neg_e ? -1 : +1;
            }

            // 在四舍五入前矫正一次
            if (value != 0 and precious != not_exist){
                value      += inc::exp10r<float_t>(precious + 1) * 5;
            }

            // 在四舍五入后还需矫正一次
            if (value >= float_t(10.0)){
                value       = 1.0;
                exp        += is_neg_e ? -1 : +1;
            }

            // 矫正后再判断指数部分的符号位
            if (auto & e = p_exp[-1]; exp < 0){
                exp         = -exp;
                e           = '-';
            }

            p_exp[0]        = item_t('0' + exp / 100);
            exp            %= 100;
            p_exp[1]        = item_t('0' + exp / 10);
            exp            %= 10;
            p_exp[2]        = item_t('0' + exp);
            p_exp          += 3;
        }
        else if (value >= float_t(inc::literal::max_value_of<u64>)){
            auto new_e      = xbitsof(u64) - 1;
            real_exp10      = uxx(inc::log10_2 * (mfxx{value}.real_exp() - new_e));
            value          *= inc::exp10r_unsafe<float_t>(real_exp10); // 相当于除以 10^real_exp10
        }
        else {
            if (precious != not_exist){
                value      += inc::exp10r<float_t>(precious + 1) * 5;
            }
            if (value < float_t(0.1)){
                dec_exp10   = uxx(inc::log10_2 * -mfxx{value}.real_exp());
                dec_exp10   = dec_exp10 <= 1 ? dec_exp10 : dec_exp10 - 1;
                value      *= inc::exp10_unsafe<float_t>(dec_exp10); // 相当于乘以 10^dec_exp10

                if (value >= float_t(1.0)){ // 浮点误差
                    value   = float_t(0.1);
                    dec_exp10  -= 1;
                }
            }
        }

        {
            auto real      = u64(value);
            auto dec       = value - float_t(real);

            do{
                p_real[0]   = real % 10 + '0';
                p_real     += 1;
                real       /= 10;
            }while(real != 0);

            // 上面 do-while 循环得到的整数部分是反序的，所以需要再 reverse 一下
            inc::reverse(real_part + 1/* 正负号占一个字符 */, p_real - real_part - 1/* 正负号占一个字符 */);

            for(uxx i = 0; dec != 0 and i < max_step; i++){
                auto real   = uxx(dec *= 10);
                p_dec[0]    = item_t(real + '0');
                p_dec      += 1;
                dec        -= real;
            }
        }

        // Abstract：
        //                   precious
        //                   |||||||||
        //              real   decimal = max(0, precious - leading_zeros)
        //                ||   |||||
        // +1.23456e-3 -> +0.001234500
        //                   ||     ||
        //                   ||     trailing_zeros = max(0, precious - leading_zeros - decimal)
        //                   leading_zeros
        //
        //               real    trailing_zeros = min(0, precious - rest)
        //               |||||   |||
        // +1.23456e3 -> +1234.56000
        //                     ||
        //                     rest = num_len - (exp + 1)
        //                     ||
        //                     decimal = min(precious, rest)
        //                     |||||
        //                     precious
        //
        //               real       precious
        //               |||||||    ||||
        // +1.23456e8 -> +123456000.0000
        //  | |||||             |||
        //  num_len             expanding_zeros
        auto real           = cxx_t(real_part, p_real - real_part);
        auto expanding_zeros= real_exp10;
        auto leading_zeros  = dec_exp10;
        auto dec_rest       = precious == not_exist ? inc::literal::max_value_of<uxx> : inc::maximum(ixx{}, ixx(precious - leading_zeros));
        auto decimal        = inc::minimum<uxx>(p_dec - decimal_part/*实际长度*/, uxx(dec_rest));
        auto dec            = cxx_t(decimal_part, decimal);
        auto trailing_zeros = precious == not_exist ? uxx{} : uxx(inc::maximum(ixx{}, ixx(precious - leading_zeros - decimal)));
        auto exp            = cxx_t(exp_part, p_exp - exp_part);
        cxx_format(real, expanding_zeros, leading_zeros, dec, trailing_zeros, exp);
    }

    template<class item_t, inc::is_float float_t, class cxx_t = cxx<item_t>>
    inline void strlize(
        float_t                 value,
        float_format_t          modes, 
        uxx                     precious, 
        auto const &            alloc){

        auto mode   = uxx(modes);
        auto with_e = uxx(mode) >= uxx(float_format_t::fmt_n) ? 
            not is_scientific_notation: is_scientific_notation;

        strlize_core<item_t>(value, with_e, precious, [&](
            cxx_t   real,
            uxx     expanding_zeros,
            uxx     leading_zeros,
            cxx_t   decimal,
            uxx     trailing_zeros,
            cxx_t   exp
        ) {
            // 不强制使用实数部分正负号
            if (not (mode & force_real_part_sign) and real[0] == '+'){
                real            = real.backward(1);
            }

            // 一般记数法
            if (not with_e){
                auto dec_len    = decimal->length + trailing_zeros;

                if (dec_len != 0){
                    dec_len    += 1; // dot
                }

                auto len        = real->length + expanding_zeros + leading_zeros + dec_len;
                auto stream     = alloc(len);
                stream.output((item_t *)real, real->length);
                stream.output('0', expanding_zeros);

                if (dec_len == 0){
                    return;
                }

                stream.output('.');
                stream.output('0', leading_zeros);
                stream.output((item_t *)decimal, decimal->length);
                stream.output('0', trailing_zeros);
                return;
            }

            // 不强制使用指数部分的正负号
            if (not (mode & force_exp_part_sign) and exp[0] == '+'){
                exp             = exp.backward(1);
            }

            auto with_dot       = decimal->length == zero and trailing_zeros == zero ? 0 : 1/*dot*/;
            auto e_len          = exp->length == zero ?
                0 : 1/*e*/ + exp->length;
            auto e              = mode & force_upper_e ? 'E' : 'e';
            auto len            = real->length + with_dot + decimal->length + trailing_zeros + e_len;
            auto stream         = alloc(len);

            if (stream.output((item_t *)real, real->length); with_dot){
                stream.output('.');
            }

            stream.output((item_t *)decimal, decimal->length);
            stream.output('0', trailing_zeros);
            stream.output(e);
            stream.output((item_t *)exp, exp->length);
        });
    }

    template<class item_t, class type_t, class cxx_t = cxx<item_t>>
    inline auto strlize_core(
        type_t                  value, 
        int_format_t            mode, 
        inc::numeration_t       radix, 
        asciis                  lut, 
        auto const &            alloc) {

        item_t   buf[64 + 1];
        item_t * ptr        = buf;
        uxx      base       = uxx(radix);

        using u_t           = inc::unsigned_type<type_t>;
        auto u              = u_t(value);
        auto is_neg         = value < 0;
        auto has_sign       = is_neg or (mode == int_format_t::fmt_sn);

        if (is_neg){
            u               = u_t(0) - u;
        }

        do {
            ptr[0]          = lut[u % base];
            ptr            += 1;
            u              /= u_t(base);
        } while(u != 0);

        auto len            = ptr - buf;
        auto total_len      = len + has_sign;
        auto stream         = alloc(total_len);

        if (is_neg){
            *ptr++          = '-';
        }
        else{
            *ptr++          = '+';
        }

        inc::reverse(buf, total_len);
        stream.output(buf, total_len);
    }

    template<class item_t, class type_t, class cxx_t = cxx<item_t>>
    inline void strlize(
        type_t                  value, 
        int_format_t            mode, 
        inc::numeration_t       radix, 
        asciis                  lut, 
        auto            const & alloc
    ) {
        using more_fit      = inc::more_fit<type_t, u64, i64>;
        strlize_core<item_t>((typename more_fit::type)value, mode, radix, lut, alloc);
    }



    template<class item_t>
    template<inc::is_integer number_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(number_t value, int_format_t fmt, inc::numeration_t radix, asciis lut, xalloc_t const & alloc) : cxx<item_t>(){
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        strlize<item_t>(value, fmt, radix, lut, allocx);
    }

    template<class item_t>
    template<inc::is_integer number_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(number_t value, int_format_t fmt, inc::numeration_t radix, xalloc_t const & alloc) : cxx<item_t>(){
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        strlize<item_t>(value, fmt, radix, lower, allocx);
    }

    template<class item_t>
    template<inc::is_integer number_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(number_t value, inc::numeration_t radix, asciis lut, xalloc_t const & alloc) : cxx<item_t>(){
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        strlize<item_t>(value, int_format_t::fmt_n, radix, lut, allocx);
    }

    template<class item_t>
    template<inc::is_integer number_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(number_t value, inc::numeration_t radix, xalloc_t const & alloc) : cxx<item_t>(){
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        strlize<item_t>(value, int_format_t::fmt_n, radix, lower, allocx);
    }

    template<class item_t>
    template<inc::is_integer number_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(number_t value, xalloc_t const & alloc) : cxx<item_t>(){
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        strlize<item_t>(value, int_format_t::fmt_n, inc::numeration_t::dec, lower, allocx);
    }

    template<class item_t>
    template<inc::is_float float_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(float_t value, float_format_t mode, uxx precious, xalloc_t const & alloc) : cxx<item_t>(){
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        strlize<item_t>(value, mode, precious, allocx);
    }

    template<class item_t>
    template<inc::is_float float_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(float_t value, uxx precious, xalloc_t const & alloc) : cxx<item_t>(){
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        strlize<item_t>(value, float_format_t::fmt_1p2e3, precious, allocx);
    }

    template<class item_t>
    template<inc::is_float float_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(float_t value, float_format_t mode, xalloc_t const & alloc) : cxx<item_t>(){
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        strlize<item_t>(value, mode, not_exist, allocx);
    }

    template<class item_t>
    template<inc::is_float float_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(float_t value, xalloc_t const & alloc) : cxx<item_t>(){
        auto abs            = inc::abs(value);
        auto limited        = float_t(inc::max_value_of<i64>);
        auto mode           = float_format_t{};
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));

        if (float_t(i64(abs)) == abs){
            strlize<item_t>(i64(value), int_format_t::fmt_n, inc::numeration_t::dec, lower, allocx);
            return;
        }
        else if (abs != 0 and (abs > limited or abs < 1e-6)){
            mode            = float_format_t::fmt_1p2e3;
        }
        else{
            mode            = float_format_t::fmt_n;
        }
        strlize<item_t>(value, mode, not_exist, allocx);
    }

    template<class item_t>
    template<class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(the_cref_t value, xalloc_t const & alloc) : cxx<item_t>(){
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        auto stream         = allocx(value.m_length);
        stream.output(value.m_ptr, value.m_length);
    }

    template<class item_t>
    template<class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(item_cref_t value, xalloc_t const & alloc) : cxx<item_t>(){
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        auto stream         = allocx(1);
        stream.output(value);
    }

    template<class item_t>
    template<class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(item_t const * value, xalloc_t const & alloc) : cxx<item_t>(){
        auto source         = the_t(value);
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        auto stream         = allocx(source.m_length);
        stream.output(source.m_ptr, source.m_length);
    }

    template<class item_t>
    template<class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(bool value, xalloc_t const & alloc) {
        if (auto allocx = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length)); value){
            auto stream     = allocx(4);
            item_t is_true[] = { 't', 'r', 'u', 'e' };
            stream.output(is_true, 4);
        }
        else{
            auto stream     = allocx(5);
            item_t is_false[] = { 'f', 'a', 'l', 's', 'e' };
            stream.output(is_false, 5);
        }
    }

    struct strlize_helper{
        template<class item_t, class cxx = cxx<item_t>>
        static void invoke(uxx high_value, uxx mid_value, uxx low_value, item_t split_char, auto const & alloc){
            enum { max_buffer = 64 };
            item_t   buf[max_buffer];
            item_t * ptr = buf;
            uxx      total_length = 0;
            uxx      list[] = { high_value, mid_value, low_value };

            for(uxx value : list){
                cxx(value, [&](uxx length){
                    if (auto need_pad_zero = length == 1; need_pad_zero){
                        ptr[0] = '0';
                        ptr++;
                    }

                    auto buffer     = ptr;
                    ptr            += length + 1/*分隔符*/;
                    ptr[-1]         = split_char;
                    return buffer;
                });
            }

            total_length            = ptr - buf - 1;
            auto stream             = alloc(total_length);
            stream.output(buf, total_length);
        }
    };


    template<class item_t>
    template<inc::is_date date_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(date_t value, xalloc_t const & alloc) : cxx<item_t>() {
        // yyyy/MM/dd
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        strlize_helper::invoke<item_t>(value->year, value->month, value->day, '/', allocx);
    }

    template<class item_t>
    template<inc::is_time time_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(time_t value, xalloc_t const & alloc) : cxx<item_t>() {
        // hh:mm::ss
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        strlize_helper::invoke<item_t>(value->hour, value->minute, value->second, ':', allocx);
    }

    template<class item_t>
    template<inc::is_datetime datetime_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(datetime_t value, xalloc_t const & alloc) : cxx<item_t>() {
        // yyyy/MM/dd hh:mm:ss
        if constexpr (inc::is_same<ret_t, the_t>){
            auto allocx                 = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
            auto stream                 = decltype(allocx(0)){};
            strlize_helper::invoke<item_t>(value->hour, value->minute, value->second, ':', [&](uxx time_length){
                strlize_helper::invoke<item_t>(value->year, value->month, value->day, '/', [&](uxx date_length){
                    auto total_length   = date_length + 1 + time_length;
                    stream              = allocx(total_length);
                    return stream;
                });

                stream.output(' ');
                return stream;
            });
        }
        else{
            auto allocx                 = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
            auto stream                 = allocx(0);
            strlize_helper::invoke<item_t>(value->year, value->month, value->day, '/', allocx);
            stream.output(' ');
            strlize_helper::invoke<item_t>(value->hour, value->minute, value->second, ':', allocx);
        }
    }
}

namespace mixc::lang_cxx::origin{
    using mixc::lang_cxx::lower;
    using mixc::lang_cxx::upper;
    using mixc::lang_cxx::float_format_t;
    using mixc::lang_cxx::int_format_t;
    using inc::numeration_t;
}

#endif
